
	var initialised = false;

	// Define a local copy of Dappr
	var dappr = function( selector, context ) {
	        // The Dappr object is actually just the init constructor 'enhanced'
	        return arguments.length === 0 ?
	            rootDappr :
	            new dapprPlugin.init( selector, context );
	},

   	// Map over dappr in case of overwrite
    _dappr = window.dappr,
	
	// Use the correct document accordingly with window argument (sandbox)
	document = window.document,

	// Use the correct navigator object
	navigator = window.navigator,
		
	rootDappr;
	
	// Device object to be filled with APIs to add/extend navigator.service
	var serviceObj = {};
	
	var dapprPlugin = dappr.prototype = {
		init: function(selector, context){
			// do some initialisation methods for general extension objects
		}
	};

	// Give the init function the Dappr prototype for later instantiation
	dapprPlugin.init.prototype = dapprPlugin;
	
	dappr.extend = dapprPlugin.extend = function() {
		    // copy reference to target object
		    var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
		
		    // Handle a deep copy situation
		    if ( typeof target === "boolean" ) {
		        deep = target;
		        target = arguments[1] || {};
		        // skip the boolean and the target
		        i = 2;
		    }
		
		    // Handle case when target is a string or something (possible in deep copy)
		    if ( typeof target !== "object" && !dappr.isFunction(target) ) {
		        target = {};
		    }

		    // extend Dappr itself if only one argument is passed
		    if ( length === i ) {
		        target = this;
		        --i;
		    }
		
		    for ( ; i < length; i++ ) {
		        // Only deal with non-null/undefined values
		        if ( (options = arguments[ i ]) != null ) {
		            // Extend the base object
		            for ( name in options ) {
		                src = target[ name ];
		                copy = options[ name ];
		
		                // Prevent never-ending loop
		                if ( target === copy ) {
		                    continue;
		                }
		
		                // Recurse if we're merging object values
		                if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
		                    var clone;
		
		                    if ( src ) {
		                        clone = src;
		                    } else if ( dappr.isArray(copy) ) {
		                        clone = [];
		                    } else if ( dappr.isObject(copy) ) {
		                        clone = {};
		                    } else {
		                        clone = copy;
		                    }
		
		                    // Never move original objects, clone them
		                    target[ name ] = dappr.extend( deep, clone, copy );
		
		                // Don't bring in undefined values
		                } else if ( copy !== undefined ) {
		                    target[ name ] = copy;
		                }
		            }
		        }
		    }
		
		    // Return the modified object
		    return target;
	};	
		
	// general purpose dappr methods
	dappr.extend({
		isString: function( obj ) {
			return (typeof obj == "string");
		},
		
    	isFunction: function( obj ) {
			return (typeof obj == "function");
	    },
	
	    isArray: function( obj ) {
		    try { // don't bother with `typeof` - just access `length` and `catch`
		        return obj.length > 0 && '0' in Object(obj);
		    }
		    catch(e) {
		        return false;
		    }
	    },
	
	    isObject: function( obj ) {
	        return (typeof obj == "object");
	    },
		
		arrayRemove: function(obj, from, to) {
		  var rest = obj.slice((to || from) + 1 || obj.length);
		  obj.length = from < 0 ? obj.length + from : from;
		  return obj.push.apply(obj, rest);
		}
							
	});
	
	// All Dappr objects should point back to these
	rootDappr = dappr(document);

	dappr.extend({
		popup: {
			popup_window:null,
			interval:null,
			interval_time:80,
			waitForPopupClose: function() {
				if(dappr.popup.isPopupClosed()) {
					dappr.popup.destroyPopup();

					// trigger features lookup to server
					dappr.oauthClient.getStatus();
				}
			},
			destroyPopup: function() {
				this.popup_window = null;
				window.clearInterval(this.interval);
				this.interval = null;
			},
			isPopupClosed: function() {
				return (!this.popup_window || this.popup_window.closed);
			},
			open: function(url, width, height) {
				this.popup_window = window.open(url,"",this.getWindowParams(width,height));
				this.interval = window.setInterval(this.waitForPopupClose, this.interval_time);
				
				return this.popup_window;
			},
			getWindowParams: function(width,height) {
				var center = this.getCenterCoords(width,height);
				return "width="+width+",height="+height+",status=1,location=1,resizable=yes,left="+center.x+",top="+center.y;
			},
			getCenterCoords: function(width,height) {
				var parentPos = this.getParentCoords();
				var parentSize = this.getWindowInnerSize();
				
				var xPos = parentPos.width + Math.max(0, Math.floor((parentSize.width - width) / 2));
				var yPos = parentPos.height + Math.max(0, Math.floor((parentSize.height - height) / 2));
				
				return {x:xPos,y:yPos};
			},
			getWindowInnerSize: function() {
				var w = 0;
				var h = 0;
				
				if ('innerWidth' in window) {
					// For non-IE
					w = window.innerWidth;
					h = window.innerHeight;
				} else {
					// For IE
					var elem = null;
					if (('BackCompat' === window.document.compatMode) && ('body' in window.document)) {
						elem = window.document.body;
					} else if ('documentElement' in window.document) {
						elem = window.document.documentElement;
					}
					if (elem !== null) {
						w = elem.offsetWidth;
						h = elem.offsetHeight;
					}
				}
				return {width:w, height:h};
			},
			getParentCoords: function() {
				var w = 0;
				var h = 0;
				
				if ('screenLeft' in window) {
					// IE-compatible variants
					w = window.screenLeft;
					h = window.screenTop;
				} else if ('screenX' in window) {
					// Firefox-compatible
					w = window.screenX;
					h = window.screenY;
			  	}
				return {width:w, height:h};
			}
		}
	});

